package com.inno.subject.domain.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.inno.subject.common.constant.RocketMQConstant;
import com.inno.subject.common.entity.PageResult;
import com.inno.subject.common.enums.IsDeletedFlagEnum;
import com.inno.subject.common.enums.SubjectLevelEnum;
import com.inno.subject.common.enums.YesOrNoEnum;
import com.inno.subject.common.error.BusinessException;
import com.inno.subject.common.utils.DateUtils;
import com.inno.subject.domain.convert.SubjectRecordsConverter;
import com.inno.subject.domain.entity.SubjectRecordsBO;
import com.inno.subject.domain.entity.SubjectSolveBO;
import com.inno.subject.domain.redis.RedisUtil;
import com.inno.subject.domain.service.SolveSubjectDomainService;
import com.inno.subject.domain.utils.ApplicationContextUtil;
import com.inno.subject.domain.utils.ChatClientUtil;
import com.inno.subject.domain.utils.CommonUtil;
import com.inno.subject.infra.basic.entity.PlannerSubjectCategory;
import com.inno.subject.infra.basic.entity.PlannerSubjectInfo;
import com.inno.subject.infra.basic.entity.PlannerSubjectRecords;
import com.inno.subject.infra.basic.entity.PlannerSubjectType;
import com.inno.subject.infra.basic.service.PlannerSubjectCategoryService;
import com.inno.subject.infra.basic.service.PlannerSubjectInfoService;
import com.inno.subject.infra.basic.service.PlannerSubjectRecordsService;
import com.inno.subject.infra.basic.service.PlannerSubjectTypeService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author sean
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SolveSubjectDomainServiceImpl implements SolveSubjectDomainService {

    private final PlannerSubjectInfoService plannerSubjectInfoService;
    private final PlannerSubjectRecordsService plannerSubjectRecordsService;
    private final PlannerSubjectCategoryService plannerSubjectCategoryService;
    private final PlannerSubjectTypeService plannerSubjectTypeService;
    private final RedisUtil redisUtil;
    private final ChatClientUtil chatClientUtil;
    private final ThreadPoolTaskExecutor subjectExecutor;
    private final RocketMQTemplate rocketMQTemplate;

    /**
     * 提交答案
     */
    @SneakyThrows
    @Override
    public SubjectSolveBO confirm(SubjectSolveBO subjectSolveBO) {
        Boolean checkDone = subjectSolveBO.getCheckDone();
        Long currentUser = subjectSolveBO.getCurrentUser();
        final String cacheKey = "subject_solve_" + currentUser;
        redisUtil.lSet(cacheKey, subjectSolveBO);

        if (!checkDone) {
            // 未完成，直接返回空对象
            return new SubjectSolveBO();
        }

        // 最终提交 计算正确率 写入数据库 清空缓存
        List<Object> cache = redisUtil.lGet(cacheKey);
        if (CollectionUtil.isEmpty(cache)) {
            return createEmptySolveBO();
        }
        List<SubjectSolveBO> solveSubjectList;

        try {
            // 将List<Object> solveSubjectLis 转为List<SubjectSolveBO>
            solveSubjectList = cache.stream()
                    .map(obj -> (SubjectSolveBO) obj)
                    .collect(Collectors.toCollection(ArrayList::new));
        } finally {
            redisUtil.lRemove(cacheKey);
        }


        List<PlannerSubjectRecords> recordsList = Collections.synchronizedList(new ArrayList<>());
        List<Long> subjectIdList = solveSubjectList.stream().map(SubjectSolveBO::getSubjectId).toList();
        List<PlannerSubjectInfo> subjectInfoList = plannerSubjectInfoService.queryByIdList(subjectIdList);

        Map<Long, PlannerSubjectInfo> subjectInfoMap = subjectInfoList.stream().collect(Collectors.toMap(PlannerSubjectInfo::getId, Function.identity()));
        List<Long> categoryIdList = subjectInfoList.stream().map(PlannerSubjectInfo::getCategoryId).toList();
        List<PlannerSubjectCategory> categoryList = plannerSubjectCategoryService.queryByIdList(categoryIdList);
        Map<Long, String> categoryNameMap = categoryList.stream()
                .collect(Collectors.toMap(PlannerSubjectCategory::getId, PlannerSubjectCategory::getCategoryName));
        int totalCount = solveSubjectList.size();
        AtomicInteger correctCount = new AtomicInteger();
        AtomicInteger errorCount = new AtomicInteger();

        // 代理对象
        SolveSubjectDomainServiceImpl proxyBean = ApplicationContextUtil.getBean(this.getClass());

        // 处理判断答案以及正确/错题数量逻辑
        CountDownLatch latch = new CountDownLatch(solveSubjectList.size());
        for (SubjectSolveBO solveBO : solveSubjectList) {

            proxyBean
                    .processorAnswerAndCount(
                            solveBO,
                            subjectInfoMap,
                            categoryNameMap,
                            currentUser,
                            recordsList,
                            correctCount,
                            errorCount, latch
                    );
        }

        boolean awaited = latch.await(20, TimeUnit.SECONDS);
        if (!awaited){
            throw new BusinessException("服务处理超时 请稍后重试");
        }

        // 如果该题用户已经作答过 以最新的为准  目前实现: 删除旧记录 保存新纪录
        proxyBean.deletedAndSave(currentUser, subjectIdList, recordsList);

        // 每次提交后 看看该用户的总正确数量是否大于了500题
        this.sendGiftsMessage(currentUser);


        return buildFinalSolveBO(totalCount, correctCount.get(), errorCount.get());
    }

    private void sendGiftsMessage(Long currentUser){
        DefaultMQProducer producer = this.rocketMQTemplate.getProducer();
        Message message = new Message();
        message.setTopic("calc-correct-count");
        // 延迟30s发送 以免异步的deletedAndSave未执行完影响计算结果
        message.setDelayTimeSec(30);
        message.setBody(currentUser.toString().getBytes());
        try {
            producer.send(message);
        } catch (Exception e) {
            log.error("发送赠送会员消息失败 : " , e);
        }
    }

    /**
     * 删除旧记录 保存新记录
     */
    @Async("subjectExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void deletedAndSave(Long currentUser, List<Long> subjectIdList, List<PlannerSubjectRecords> recordsList) {
        List<PlannerSubjectRecords> currentUserRecordList = plannerSubjectRecordsService.queryByUserIdAndSubjectIdList(currentUser, subjectIdList);
        List<Long> needDeleteList = new ArrayList<>();
        for (PlannerSubjectRecords records : recordsList) {
            Long subjectId = records.getSubjectId();
            for (PlannerSubjectRecords record : currentUserRecordList) {
                if (record.getSubjectId().equals(subjectId)) {
                    needDeleteList.add(record.getId());
                }
            }
        }
        if (CollectionUtil.isNotEmpty(needDeleteList)) {
            this.plannerSubjectRecordsService.deleteBatch(needDeleteList);
        }

        if (CollectionUtil.isNotEmpty(recordsList)) {
            plannerSubjectRecordsService.insertBatch(recordsList);
        }
    }

    /**
     * 处理判断答案以及正确/错题数量逻辑
     */
    @Async("subjectExecutor")
    public void processorAnswerAndCount(SubjectSolveBO solveBO,
                                        Map<Long, PlannerSubjectInfo> subjectInfoMap,
                                        Map<Long, String> categoryNameMap,
                                        Long currentUser,
                                        List<PlannerSubjectRecords> recordsList,
                                        AtomicInteger correctCount,
                                        AtomicInteger errorCount,
                                        CountDownLatch latch
    ) {


        PlannerSubjectRecords records = new PlannerSubjectRecords();
        PlannerSubjectInfo plannerSubjectInfo = subjectInfoMap.getOrDefault(solveBO.getSubjectId(), new PlannerSubjectInfo());
        String categoryName = categoryNameMap.getOrDefault(plannerSubjectInfo.getCategoryId(), "");
        try {
            if (isAnswerCorrect(plannerSubjectInfo, solveBO)) {
                buildCorrectRecords(records, plannerSubjectInfo, solveBO, currentUser, categoryName);
                recordsList.add(records);
                correctCount.getAndIncrement();
            } else {
                buildIncorrectRecords(records, plannerSubjectInfo, solveBO, currentUser, categoryName);
                recordsList.add(records);
                errorCount.getAndIncrement();
            }
        } catch (Exception e) {
            log.error("processAnswerCorrectFuture.Exception{}", e.getMessage(), e);
        } finally {
            latch.countDown();
        }


    }

    /**
     * 答题记录列表
     */
    @Override
    public PageResult<SubjectRecordsBO> queryRecord(SubjectRecordsBO subjectRecordsBO) {
        PlannerSubjectRecords plannerSubjectRecords = SubjectRecordsConverter.INSTANCE.convertBOToEntity(subjectRecordsBO);
        plannerSubjectRecords.setUserId(subjectRecordsBO.getCurrentUserId());
        PageHelper.startPage(subjectRecordsBO.getPage(), subjectRecordsBO.getPageSize());

        Page<PlannerSubjectRecords> entityPage = this.plannerSubjectRecordsService.queryPage(plannerSubjectRecords);

        // 需要什么字段？  题目名称  题目类型 题目难度
        List<PlannerSubjectRecords> entityResult = entityPage.getResult();
        if (CollectionUtil.isEmpty(entityResult)) {
            return PageResult.of(Collections.emptyList(), 0L);
        }
        List<SubjectRecordsBO> boList = SubjectRecordsConverter.INSTANCE.convertEntityListToBOList(entityResult);
        List<Long> subjectIdList = boList.stream().map(SubjectRecordsBO::getSubjectId).toList();
        List<PlannerSubjectInfo> subjectInfoList = this.plannerSubjectInfoService.queryByIdList(subjectIdList);
        Map<Long, PlannerSubjectInfo> subjectInfoMap = subjectInfoList.stream()
                .collect(Collectors.toMap(PlannerSubjectInfo::getId, Function.identity()));

        List<PlannerSubjectType> types = plannerSubjectTypeService.queryByIdList(boList.stream().map(SubjectRecordsBO::getSubjectTypeId).toList());
        Map<Long, PlannerSubjectType> subjectTypeMap = types.stream().collect(Collectors.toMap(PlannerSubjectType::getId, Function.identity()));
        boList.parallelStream().forEach(bo -> {
            PlannerSubjectInfo subjectInfo = subjectInfoMap.get(bo.getSubjectId());
            bo.setSubjectName(subjectInfo.getSubjectName());
            bo.setSubjectLevelName(SubjectLevelEnum.getDescByCode(subjectInfo.getSubjectLevel()));
            bo.setSubjectTypeName(subjectTypeMap.get(bo.getSubjectTypeId()).getTypeName());
//            bo.setIsRight(bo.getIsRight().equals(YesOrNoEnum.YES.getCode()) ? YesOrNoEnum.YES.getValue() : YesOrNoEnum.NO.getValue());
        });
        return PageResult.of(boList, entityPage.getTotal());
    }

    /**
     * Used to determine if the answer is correct
     *
     * @return true or false
     */
    private boolean isAnswerCorrect(PlannerSubjectInfo plannerSubjectInfo, SubjectSolveBO solveBO) {
        if (plannerSubjectInfo.getSubjectType() == 2) {
            // 选择题
            String recordAnswer = solveBO.getSubjectAnswer();
            List<String> answerList = JSON.parseArray(plannerSubjectInfo.getSubjectAnswer(), String.class);
            List<String> recordAnswerList = JSON.parseArray(recordAnswer, String.class);
            return recordAnswerList != null
                    && recordAnswerList.size() == answerList.size()
                    && new HashSet<>(recordAnswerList).containsAll(answerList);
        } else {
            String prompt = chatClientUtil.buildPrompt(plannerSubjectInfo.getSubjectName(), solveBO.getSubjectAnswer());
            String response = chatClientUtil.call(prompt);
            log.info("ai答案判断:{}", response);

            return YesOrNoEnum.YES.getValue().equals(response);
        }
    }

    private void buildCorrectRecords(PlannerSubjectRecords records, PlannerSubjectInfo plannerSubjectInfo, SubjectSolveBO solveBO, Long currentUser, String categoryName) {
        buildRecords(records, plannerSubjectInfo, solveBO, currentUser, YesOrNoEnum.YES.getCode(), categoryName);
    }

    private void buildIncorrectRecords(PlannerSubjectRecords records, PlannerSubjectInfo plannerSubjectInfo, SubjectSolveBO solveBO, Long currentUser, String categoryName) {
        buildRecords(records, plannerSubjectInfo, solveBO, currentUser, YesOrNoEnum.NO.getCode(), categoryName);
    }

    private void buildRecords(PlannerSubjectRecords records, PlannerSubjectInfo plannerSubjectInfo, SubjectSolveBO solveBO, Long currentUser, String isRight, String categoryName) {
        Date nowDate = DateUtils.getNowDate();
        records.setId(IdUtil.getSnowflakeNextId());
        records.setCreateBy(currentUser);
        records.setCreateTime(nowDate);
        records.setIsRight(isRight);
        if (plannerSubjectInfo.getSubjectType() == 1) {
            records.setRightAnswer(plannerSubjectInfo.getSubjectParse());
        } else {
            records.setRightAnswer(plannerSubjectInfo.getSubjectAnswer());
        }
        records.setRecordAnswer(solveBO.getSubjectAnswer());
        records.setSubjectId(solveBO.getSubjectId());
        records.setSubjectTypeId(plannerSubjectInfo.getSubjectType());
        records.setUserId(currentUser);
        records.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode().toString());
        records.setCategoryName(categoryName);
    }

    private SubjectSolveBO createEmptySolveBO() {
        SubjectSolveBO subjectSolveBO = new SubjectSolveBO();
        subjectSolveBO.setTotalCount(0);
        subjectSolveBO.setCorrectCount(0);
        subjectSolveBO.setCorrectRate(BigDecimal.ZERO);
        subjectSolveBO.setErrorCount(0);
        return subjectSolveBO;
    }

    /**
     * 构建最终结果对象
     *
     * @param totalCount   总数
     * @param correctCount 正确数
     * @param errorCount   错误数
     * @return 结果对象
     */
    private SubjectSolveBO buildFinalSolveBO(int totalCount, int correctCount, int errorCount) {
        SubjectSolveBO subjectSolveBO = new SubjectSolveBO();
        subjectSolveBO.setTotalCount(totalCount);
        subjectSolveBO.setErrorCount(errorCount);
        subjectSolveBO.setCorrectCount(correctCount);
        subjectSolveBO.setCorrectRate(CommonUtil.calcRate(totalCount, correctCount));
        subjectSolveBO.setErrorRate(CommonUtil.calcRate(totalCount, errorCount));
        return subjectSolveBO;
    }


}
